using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Yoozoo.Gameplay.Liberty.AI
{
    public class PhysicsUtils
    {
        // 最大检测数量
        public const int MAX_NOALLOC_LENGTH = 25;

        // 射线最大检测数量
        public const int MAX_RAYCAST_NOALLOC_LENGTH = 10;
        
        
        private static Collider[] _Results = new Collider[MAX_NOALLOC_LENGTH];

        private static RaycastHit _RaycastHitNull = new RaycastHit();
        private static RaycastHit[] _RaycastHits = new RaycastHit[MAX_RAYCAST_NOALLOC_LENGTH];
        
        /// <summary>
        /// 射线检测 (NonAlloc)
        /// </summary>
        /// <param name="resultCount"></param>
        /// <param name="origin"></param>
        /// <param name="direction"></param>
        /// <param name="maxDistance"></param>
        /// <param name="layerNames"></param>
        /// <returns></returns>
        public static int RaycastNonAlloc(out RaycastHit[] raycastHits, Vector3 origin, Vector3 direction, float maxDistance, string[] layerNames)
        {
            int count = UnityEngine.Physics.RaycastNonAlloc(origin, direction, _RaycastHits, maxDistance, GetLayerMask(layerNames));
            raycastHits = _RaycastHits;
            return count;
        }
        public static int RaycastNonAlloc(out RaycastHit[] raycastHits, Vector3 origin, Vector3 direction, float maxDistance, int layerMask)
        {
            int count = UnityEngine.Physics.RaycastNonAlloc(origin, direction, _RaycastHits, maxDistance, layerMask);
            raycastHits = _RaycastHits;
            return count;
        }
        public static bool RaycastNonAlloc(out RaycastHit raycastHit, Vector3 origin, Vector3 direction, float maxDistance, string[] layerNames)
        {
            int count = UnityEngine.Physics.RaycastNonAlloc(origin, direction, _RaycastHits, maxDistance, GetLayerMask(layerNames));
            // 获取最近的结果
            if (count > 0)
            {
                float minDistance = _RaycastHits[0].distance;
                int minIndex = 0;
                float distance;
                for (int i = 1; i < count; ++i)
                {
                    distance = _RaycastHits[i].distance;
                    if (distance < minDistance)
                    {
                        minDistance = distance;
                        minIndex = i;
                    }
                }

                raycastHit = _RaycastHits[minIndex];
                return true;
            }

            raycastHit = _RaycastHitNull;
            return false;
        }
        public static bool RaycastNonAlloc(out RaycastHit raycastHit, Vector3 origin, Vector3 direction, float maxDistance, string layerName)
        {
            int count = UnityEngine.Physics.RaycastNonAlloc(origin, direction, _RaycastHits, maxDistance, GetLayerMask(layerName));
            // 获取最近的结果
            if (count > 0)
            {
                float minDistance = _RaycastHits[0].distance;
                int minIndex = 0;
                float distance;
                for (int i = 1; i < count; ++i)
                {
                    distance = _RaycastHits[i].distance;
                    if (distance < minDistance)
                    {
                        minDistance = distance;
                        minIndex = i;
                    }
                }

                raycastHit = _RaycastHits[minIndex];
                return true;
            }

            raycastHit = _RaycastHitNull;
            return false;
        }
        
        /// <summary>
        /// 射线检测
        /// </summary>
        /// <param name="raycastHit"></param>
        /// <param name="origin"></param>
        /// <param name="direction"></param>
        /// <param name="maxDistance"></param>
        /// <param name="layerNames"></param>
        /// <returns></returns>
        public static bool Raycast(out RaycastHit raycastHit, Vector3 origin, Vector3 direction, float maxDistance, string[] layerNames)
        {
            return UnityEngine.Physics.Raycast(origin, direction, out raycastHit, maxDistance, GetLayerMask(layerNames));
        }
        public static bool Raycast(out RaycastHit raycastHit, Vector3 origin, Vector3 direction, float maxDistance, string layerName)
        {
            return UnityEngine.Physics.Raycast(origin, direction, out raycastHit, maxDistance, GetLayerMask(layerName));
        }

        /// <summary>
        /// 检测Box区域 (NonAlloc)
        /// </summary>
        /// <param name="resultCount"></param>
        /// <param name="center"></param>
        /// <param name="halfExtents"></param>
        /// <param name="orientation"></param>
        /// <param name="layerNames"></param>
        /// <returns></returns>
        public static Collider[] OverlapBoxNonAlloc(out int resultCount, Vector3 center, Vector3 halfExtents, Quaternion orientation, string[] layerNames)
        {
            resultCount = UnityEngine.Physics.OverlapBoxNonAlloc(center, halfExtents, _Results, orientation, GetLayerMask(layerNames));
            return _Results;
        }
        public static Collider[] OverlapBoxNonAlloc(out int resultCount, Vector3 center, Vector3 halfExtents, Quaternion orientation, string layerName)
        {
            resultCount = UnityEngine.Physics.OverlapBoxNonAlloc(center, halfExtents, _Results, orientation, GetLayerMask(layerName));
            return _Results;
        }
        
        /// <summary>
        /// 检测球形区域 (NonAlloc)
        /// </summary>
        /// <param name="resultCount"></param>
        /// <param name="position"></param>
        /// <param name="radius"></param>
        /// <param name="layerNames"></param>
        /// <returns></returns>
        public static Collider[] OverlapSphereNonAlloc(out int resultCount, Vector3 position, float radius, string[] layerNames)
        {
            resultCount = UnityEngine.Physics.OverlapSphereNonAlloc(position, radius,  _Results, GetLayerMask(layerNames));
            return _Results;
        }
        public static Collider[] OverlapSphereNonAlloc(out int resultCount, Vector3 position, float radius, string layerName)
        {
            resultCount = UnityEngine.Physics.OverlapSphereNonAlloc(position, radius,  _Results, GetLayerMask(layerName));
            return _Results;
        }
        
        /// <summary>
        /// 检测扇形区域
        /// </summary>
        /// <param name="resultCount"></param>
        /// <param name="position"></param>
        /// <param name="radius"></param>
        /// <param name="forward"></param>
        /// <param name="degree"></param>
        /// <param name="layerName"></param>
        /// <returns></returns>
        public static Collider[] OverlapFanNonAlloc(out int resultCount, Vector3 position, float radius, Vector3 forward, float degree, string[] layerNames)
        {
            resultCount = UnityEngine.Physics.OverlapSphereNonAlloc(position, radius,  _Results, GetLayerMask(layerNames));

            // 判断扇区角度
            int newResultCount = 0;
            float halfDegree = degree / 2f;
            for (int i = 0; i < resultCount; ++i)
            {
                Collider collider = _Results[i];
                Vector3 direction = collider.transform.position - position;
                direction.y = 0;
                float angle = Vector3.Angle(direction, forward);
                if (angle <= halfDegree)
                {
                    _Results[newResultCount] = collider;
                    newResultCount++;
                }
            }
            resultCount = newResultCount;
            
            return _Results;
        }
        public static Collider[] OverlapFanNonAlloc(out int resultCount, Vector3 position, float radius, Vector3 forward, float degree, string layerName)
        {
            resultCount = UnityEngine.Physics.OverlapSphereNonAlloc(position, radius,  _Results, GetLayerMask(layerName));

            // 判断扇区角度
            int newResultCount = 0;
            float halfDegree = degree / 2f;
            for (int i = 0; i < resultCount; ++i)
            {
                Collider collider = _Results[i];
                Vector3 direction = collider.transform.position - position;
                direction.y = 0;
                float angle = Vector3.Angle(direction, forward);
                if (angle <= halfDegree)
                {
                    _Results[newResultCount] = collider;
                    newResultCount++;
                }
            }
            resultCount = newResultCount;
            
            return _Results;
        }
        
        /// <summary>
        /// 检测胶囊体区域 (NonAlloc)
        /// </summary>
        /// <param name="resultCount"></param>
        /// <param name="point0"></param>
        /// <param name="point1"></param>
        /// <param name="radius"></param>
        /// <param name="layerNames"></param>
        /// <returns></returns>
        public static Collider[] OverlapCapsule(out int resultCount, Vector3 point0, Vector3 point1, float radius, string[] layerNames)
        {
            resultCount = UnityEngine.Physics.OverlapCapsuleNonAlloc(point0, point1, radius, _Results, GetLayerMask(layerNames));
            return _Results;
        }
        public static Collider[] OverlapCapsule(out int resultCount, Vector3 point0, Vector3 point1, float radius, string layerName)
        {
            resultCount = UnityEngine.Physics.OverlapCapsuleNonAlloc(point0, point1, radius, _Results, GetLayerMask(layerName));
            return _Results;
        }
        
        /// <summary>
        /// 检测Box区域
        /// </summary>
        /// <param name="center"></param>
        /// <param name="halfExtents"></param>
        /// <param name="orientation"></param>
        /// <param name="layerNames"></param>
        /// <returns></returns>
        public static Collider[] OverlapBox(Vector3 center, Vector3 halfExtents, Quaternion orientation, string[] layerNames)
        {
            return UnityEngine.Physics.OverlapBox(center, halfExtents, orientation, GetLayerMask(layerNames));
        }
        public static Collider[] OverlapBox(Vector3 center, Vector3 halfExtents, Quaternion orientation, string layerName)
        {
            return UnityEngine.Physics.OverlapBox(center, halfExtents, orientation, GetLayerMask(layerName));
        }

        /// <summary>
        /// 检测球形区域
        /// </summary>
        /// <param name="position"></param>
        /// <param name="radius"></param>
        /// <param name="layerNames"></param>
        /// <returns></returns>
        public static Collider[] OverlapSphere(Vector3 position, float radius, string[] layerNames)
        {
            return UnityEngine.Physics.OverlapSphere(position, radius, GetLayerMask(layerNames));
        }
        public static Collider[] OverlapSphere(Vector3 position, float radius, string layerName)
        {
            return UnityEngine.Physics.OverlapSphere(position, radius, GetLayerMask(layerName));
        }
        
        /// <summary>
        /// 检测胶囊体区域
        /// </summary>
        /// <param name="point0"></param>
        /// <param name="point1"></param>
        /// <param name="radius"></param>
        /// <param name="layerNames"></param>
        /// <returns></returns>
        public static Collider[] OverlapCapsule(Vector3 point0, Vector3 point1, float radius, string[] layerNames)
        {
            return UnityEngine.Physics.OverlapCapsule(point0, point1, radius, GetLayerMask(layerNames));
        }
        public static Collider[] OverlapCapsule(Vector3 point0, Vector3 point1, float radius, string layerName)
        {
            return UnityEngine.Physics.OverlapCapsule(point0, point1, radius, GetLayerMask(layerName));
        }

        /// <summary>
        /// 计算LayerMask
        /// </summary>
        /// <param name="layerNames"></param>
        /// <returns></returns>
        public static int GetLayerMask(string[] layerNames)
        {
            int layerMask = -1;
            if (layerNames.Length > 0)
            {
                layerMask = 1 << LayerMask.NameToLayer(layerNames[0]);
            }
            for (int i=1; i<layerNames.Length; ++i)
            {
                layerMask |= 1 << LayerMask.NameToLayer(layerNames[i]);
            }

            return layerMask;
        }
        
        public static int GetLayerMask(string layerName)
        {
            return 1 << LayerMask.NameToLayer(layerName);
        }
        
    }

}

